/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file   fatfs.c
  * @brief  Code for fatfs applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
#include "fatfs.h"

uint8_t retUSER;    /* Return value for USER */
char USERPath[4];   /* USER logical drive path */
FATFS USERFatFS;    /* File system object for USER logical drive */
FIL USERFile;       /* File object for USER */

/* USER CODE BEGIN Variables */
#define LOG_TAG "FATFS"
/* USER CODE END Variables */

void MX_FATFS_Init(void)
{
  /*## FatFS: Link the USER driver ###########################*/
  retUSER = FATFS_LinkDriver(&USER_Driver, USERPath);

  /* USER CODE BEGIN Init */
  /* additional user code for init */
  /* USER CODE END Init */
}

/**
  * @brief  Gets Time from RTC
  * @param  None
  * @retval Time in DWORD
  */
DWORD get_fattime(void)
{
  /* USER CODE BEGIN get_fattime */
  return 0;
  /* USER CODE END get_fattime */
}

/* USER CODE BEGIN Application */
#include "system.h"
#include "usart_drv.h"
#include "string.h"
#include "stdio.h"
static DIR dir;
static FILINFO finfo;

/**
 * @brief 文件系统初始化
 * @note  输出文件系统使用率
 */
void file_system_init(void)
{
    DWORD fre_clust, total_sect, sect_size;
    FATFS *fs = &USERFatFS;
    double total_size = 0, used_size = 0, use_rate;
    char* size_unit[4] = {"GB", "MB", "KB", "B"};
    uint8_t total_size_unit, used_size_unit;
    FRESULT res = f_mount(&USERFatFS, (TCHAR const*)USERPath, 1);
    if (res == FR_NO_FILESYSTEM)
    {
        log_a("文件系统进行格式化 ");
        BYTE *work = (BYTE *)pvPortMalloc(sizeof(BYTE) * 4096);
        if (work == NULL)
        {
            log_a("内存申请失败 ");
            return;
        }
        res = f_mkfs((TCHAR const*)USERPath, FM_FAT, 0, work, 4096);
        if (res == FR_OK)
        {
            log_a("文件系统格式化成功 ");
            res = f_mount(NULL, (TCHAR const*)USERPath, 1);
            res = f_mount(&USERFatFS, (TCHAR const*)USERPath, 1);
        }
        else
        {
            log_a("文件系统格式化失败 ");
        }
    }
    else if (res != FR_OK)
    {
        log_a("文件系统挂载失败 ");
    }
    else
    {
        log_w("文件系统挂载成功 ");
    }

    res = f_getfree((TCHAR const*)USERPath, &fre_clust, &fs);
    if(res == FR_OK)
    {
        total_sect = (fs->n_fatent - 2) * fs->csize;
        sect_size = fs->ssize;
        total_size = (double)total_sect * (double)sect_size;
    }
    res = f_findfirst(&dir, &finfo, USERPath, "*");
    if (res != FR_OK)
    {
        log_e("初始化扫盘失败 %d", __LINE__);
        return;
    }
    while (res == FR_OK && finfo.fname[0])
    {
        if (finfo.fattrib & AM_DIR) // 对于文件夹，递归计算大小
        {
            used_size += file_get_dir_size(USERPath, finfo.fname);
        } 
        else // 对于文件，直接使用 finfo.fsize
        {
            
            used_size += (double)finfo.fsize;
        }
        res = f_findnext(&dir, &finfo);
    }
    f_closedir(&dir);
    use_rate = used_size / total_size;
    if (total_size > 1024 * 1024 * 1024)
    {
        total_size = total_size / (1024 * 1024 * 1024);
        total_size_unit = 0;
    }
    else if(total_size > 1024 * 1024)
    {
        total_size = total_size / (1024 * 1024);
        total_size_unit = 1;
    }
    else if(total_size > 1024)
    {
        total_size = total_size / 1024;
        total_size_unit = 2;
    }
    else
    {
        total_size_unit = 3;
    }

    if (used_size > 1024 * 1024 * 1024)
    {
        used_size = used_size / (1024 * 1024 * 1024);
        used_size_unit = 0;
    }
    else if(used_size > 1024 * 1024)
    {
        used_size = used_size / (1024 * 1024);
        used_size_unit = 1;
    }
    else if(used_size > 1024)
    {
        used_size = used_size / 1024;
        used_size_unit = 2;
    }
    else
    {
        used_size_unit = 3;
    }
    if (total_size > 0 && used_size > 0)
    {
        log_i("文件系统容量: %.2f%s/%.2f%s, 使用率: %.2f%%", used_size, size_unit[used_size_unit], total_size, size_unit[total_size_unit], use_rate * 100);
    }
}


/**
 * @brief 获取文件夹大小
 * @note  通过递归获取文件夹大小
 * @param path 路径
 * @param dirname 文件夹名
 * @return 文件夹总大小
 */
double file_get_dir_size(const char *path, const char *dirname) 
{
    DIR dir;
    FILINFO finfo;
    double total_size = 0;
    char *full_path = NULL;
    FRESULT res;
    size_t path_len = strlen(path) + strlen(dirname) + 2; // 添加 '/' 的长度 和 '/0'

    full_path = (char *)pvPortMalloc(path_len);
    if (full_path == NULL)
    {
        log_e("内存申请 失败 ");
        return 0;
    }
    memset(full_path, 0, path_len);

    // 构造完整路径
    snprintf(full_path, path_len, "%s%s%s", path, path[strlen(path) - 1] == '/' ? "" : "/", dirname);
    
    if (f_opendir(&dir, full_path) == FR_OK) 
    {
        res = f_findfirst(&dir, &finfo, full_path, "*");
        while (res == FR_OK && finfo.fname[0]) 
        {
            if (finfo.fattrib & AM_DIR) 
            {
                // 跳过 . 和 .. 目录
                if (strcmp(finfo.fname, ".") == 0 || strcmp(finfo.fname, "..") == 0) 
                {
                    continue;
                }
                // 递归计算子文件夹大小
                total_size += file_get_dir_size(full_path, finfo.fname);
            } 
            else 
            {
                // 累加文件大小
                total_size += (double)finfo.fsize;
            }
            f_findnext(&dir, &finfo);
        }
        f_closedir(&dir);
    }
    vPortFree(full_path);
    return total_size;
}

/**
 * @brief  扫描路径下所有文件并输出名字和大小
 * @param[in] drive 盘符
 * @param[out] info_output 输出信息
 *             可以为空 直接使用log_v输出
 * @param[in] info_output_len 输出信息最大长度
 * @return 0:成功, -1:失败
 */
int file_dir_scan(const char * path, char* info_output, size_t info_output_len)
{
    FRESULT res;
    size_t output_len = 0;
    double file_size = 0;
    char f_size_str[16];

    res = f_findfirst(&dir, &finfo, path, "*");
    if (res != FR_OK)
    {
        log_e("打开目录失败 (%d)", res);
        return -1;
    }
    while (res == FR_OK && finfo.fname[0])
    {
        if (finfo.fattrib & AM_DIR) // 对于文件夹，递归计算大小
        {
            file_size = file_get_dir_size(path, finfo.fname);
        } 
        else // 对于文件，直接使用 finfo.fsize
        {
            
            file_size = (double)finfo.fsize;
        }
        if (file_size < 1024.0f)
        {
            snprintf(f_size_str, sizeof(f_size_str), "%.2fB", file_size);
        }
        else if (file_size < 1024.0f * 1024.0f)
        {
            file_size = file_size / 1024.0;
            snprintf(f_size_str, sizeof(f_size_str), "%.2fKB", file_size);
        }
        else
        {
            file_size = file_size / 1024.0;
            snprintf(f_size_str, sizeof(f_size_str), "%.2fMB", file_size);
        }

        if (info_output != NULL)
        {
            if (output_len < info_output_len)
            {
                output_len += snprintf(info_output + output_len, info_output_len - output_len, "%-10s %s\r\n", finfo.fname, f_size_str);
            }
            else
            {
                log_w("写入缓冲区溢出 %d > %d", output_len, info_output_len);
                return -1;
            }
        }
        else
        {
            log_v("%-10s %s\r\n", finfo.fname, f_size_str);
        }

        res = f_findnext(&dir, &finfo);
    }
    f_closedir(&dir);
    return 0;
}

/**
 * @brief 打开文件内容
 * @param path 文件路径
 * @param file_name 文件名
 * @param info_output 文件内容输出缓存
 * @param info_output_len 文件内容输出缓存长度
 * @return 0 成功 -1 失败
 */
int file_cat_print(const char *path, const char *file_name, char *info_output, size_t info_output_len)
{
    FRESULT res;
    UINT bytesRead;
    char file_path[128] = {0};

    if (info_output == NULL)
    {
        log_e("info_output is NULL");
    }

    // 构造完整路径
    snprintf(file_path, sizeof(file_path), "%s%s%s", path, path[strlen(path) - 1] == '/' ? "" : "/", file_name);

    res = f_open(&USERFile, file_path, FA_READ);
    if (res != RES_OK)
    {
        log_e("打开文件失败 (%d)", res);
        return -1;
    }
    
    log_v("--------------------------File Begin--------------------------");
    while (1)
    {
        res = f_read(&USERFile, info_output, info_output_len - 1, &bytesRead);
        if (res != RES_OK)
        {
            log_e("读取文件失败 (%d)", res);
            f_close(&USERFile);
            return -1;
        }
        info_output[bytesRead] = '\0';
        if (bytesRead > 0)
        {
            uart_log_print("%s", info_output);//此处打印不能加\n 否则会打断大文件的打印
        }

        memset(info_output, 0, info_output_len);

        if (bytesRead < info_output_len - 1)
        {
            break;
        }
    }
    f_close(&USERFile);
    log_v("\r\n--------------------------File   End--------------------------");
    return 0;
}

FRESULT file_delete_recursive(const char *path)
{
    FRESULT res;
    char *sub_path = NULL;
    size_t path_len = 0;
    if (path == NULL)
    {
        return FR_INVALID_NAME;
    }

    res = f_unlink(path);
    if (res == FR_OK || res == FR_NO_FILE)
    {
        return FR_OK;
    }
    if (res != FR_DENIED)
    {
        return res;
    }

    res = f_opendir(&dir, path);
    if (res != FR_OK)
    {
        return res;
    }

    //到这里说明path是目录而不是文件
    res = f_findfirst(&dir, &finfo, path, "*");
    while (res == FR_OK && finfo.fname[0])//文件夹下还有内容
    {
        // 跳过 . 和 .. 目录
        if (strcmp(finfo.fname, ".") == 0 || strcmp(finfo.fname, "..") == 0) 
        {
            continue;
        }
        path_len = strlen(path) + strlen(finfo.fname) + 2;
        sub_path = (char *)pvPortMalloc(path_len);
        if (sub_path == NULL)
        {
            f_closedir(&dir);
            return FR_INVALID_NAME;
        
        }
        memset(sub_path, 0, path_len);
        snprintf(sub_path, path_len, "%s%s%s", path, path[strlen(path) - 1] == '/' ? "" : "/", finfo.fname);// 拼接子目录
        res = file_delete_recursive(sub_path);
    }
    f_closedir(&dir);
    if (sub_path != NULL)
    {
        vPortFree(sub_path);
    }
    // 删除空文件夹
    res = f_unlink(path);
}

int file_create_file(const char *path, const char *file_name, const uint8_t *data, size_t size)
{
    FRESULT res;
    size_t path_len = strlen(path) + strlen(file_name) + 1;
    char *file_path = (char *)pvPortMalloc(path_len);
    if (file_path == NULL)
    {
        log_e("内存申请失败 %d", __LINE__);
        return -1;
    }
    memset(file_path, 0, path_len);
    snprintf(file_path, path_len, "%s%s%s", path, path[strlen(path) - 1] == '/' ? "" : "/", file_name);
    if (data != NULL)
    {
        UINT bw;
        res = f_open(&USERFile, file_path, FA_CREATE_NEW | FA_WRITE);
        if (res == FR_OK)
        {
            res = f_write(&USERFile, data, size, &bw);
            if (res != FR_OK)
            {
                log_e("写入文件%s 失败 ", file_path);
            }
            f_close(&USERFile);
        }
        else if (res == FR_EXIST)
        {
            log_e("文件%s 已存在 ", file_path);
        }
        else
        {
            log_e("文件%s 创建失败 ", file_path) ;
        }
    }
    else
    {
        res = f_open(&USERFile, file_path, FA_CREATE_NEW);    
        if (res == FR_OK)
        {
            log_i("文件%s 创建成功 ", file_path);
        }
        else if (res == FR_EXIST)
        {
            log_e("文件%s已存在 ", file_path);
        }
        else
        {
            log_e("文件%s创建失败 ", file_path) ;
        }
    }
}

void file_system_test(void)
{
  FRESULT res = f_open(&USERFile, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
  UINT bw;
  if (res == FR_OK)
  {
    log_w("文件打开成功 ");
    res = f_write(&USERFile, "Hello World 2025-8-3", 20, &bw); 
    if (res == FR_OK)
    {
      log_i("写入成功 ");
      
    }
    else
    {
      log_e("写入失败 ");
    }
    f_close(&USERFile);
  }
  else
  {
    log_e("文件打开失败 ");
  }

  uint8_t readBuf[100] = {0};
  f_open(&USERFile, "0:/test.txt", FA_OPEN_EXISTING | FA_READ);
  if (res == FR_OK)
  { 
    log_i("文件打开成功 ");
    res= f_read(&USERFile, readBuf, 100, &bw);
    if (res == FR_OK)
    {
      log_i("读取成功 %d 字节", bw);
      log_i("读取内容：%s", readBuf);
    }
    else
    {
      log_e("读取失败 ");
    }
    f_close(&USERFile);
  }
  else
  {
    log_e("文件打开失败 ");
  }

}

void file_system_test2(void)
{
  FRESULT res = RES_ERROR;
  DIR dir;
  res = f_opendir(&dir, "0:/logs");
  if (res != FR_OK)
  {
    log_i("目录不存在，创建目录logs ");
    res = f_mkdir("0:/logs");
    if (res == FR_OK)
    {
      log_i("创建成功 ");
    }
    else
    {
      log_e("创建失败 ");
      return;
    }
  }
  res = f_opendir(&dir, "0:/logs");
  if (res == FR_OK)
  {
    log_i("目录打开成功 ");
    FILINFO fno;
    res = f_findfirst(&dir, &fno, "0:/logs", "*");
    while (res == FR_OK && fno.fname[0])
    {
      log_i("文件名：%s ", fno.fname);
      res = f_findnext(&dir, &fno);
    }
    res = f_open(&USERFile, "0:/logs/log.txt", FA_OPEN_EXISTING  | FA_WRITE);
    if (res == FR_OK)
    {
      log_i("打开成功 ");
      UINT bw;
      f_close(&USERFile);
    }
    else
    {
      log_e("创建失败 ");
      return;
    }
    f_closedir(&dir);
  }
  else
  {
    log_e("打开失败 ");
  }
}







/* USER CODE END Application */
